package com.yida.wy.basic.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.yida.common.core.exception.ServiceException;
import com.yida.common.core.utils.DateUtils;
import com.yida.common.core.utils.StringUtils;
import com.yida.common.core.utils.bean.BeanValidators;
import com.yida.common.security.utils.SecurityUtils;
import com.yida.wy.basic.service.IWyImpBasicDataService;
import com.yida.wy.common.constant.WyConstants;
import com.yida.wy.common.constant.WyCustomImportConstants;
import com.yida.wy.common.domain.WyBaseBuild;
import com.yida.wy.common.domain.WyBaseCommunity;
import com.yida.wy.common.domain.WyBaseHouse;
import com.yida.wy.common.domain.WyBaseOwner;
import com.yida.wy.common.domain.vo.WyBaseBuildExcelImpVO;
import com.yida.wy.common.domain.vo.WyBaseCommunityExcelImpVO;
import com.yida.wy.common.domain.vo.WyBaseHouseOwnerExcelImpVO;
import com.yida.wy.common.excel.core.ExcelResult;
import com.yida.wy.common.excel.utils.ExcelUtil;
import com.yida.wy.common.service.*;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;

import static com.yida.wy.common.domain.table.WyBaseBuildTableDef.WY_BASE_BUILD;
import static com.yida.wy.common.domain.table.WyBaseCommunityTableDef.WY_BASE_COMMUNITY;
import static com.yida.wy.common.domain.table.WyBaseHouseTableDef.WY_BASE_HOUSE;
import static com.yida.wy.common.domain.table.WyBaseOwnerTableDef.WY_BASE_OWNER;

@Service
public class WyImpBasicDataServiceImpl implements IWyImpBasicDataService {

    private static final Logger log = LoggerFactory.getLogger(WyImpBasicDataServiceImpl.class);

    @Resource
    protected Validator validator;

    @Resource
    private IWyBaseCommunityService communityService;

    @Resource
    private IWyBaseBuildService buildService;

    @Resource
    private IWyBaseHouseService houseService;

    @Resource
    private IWyBaseOwnerService ownerService;

    @Resource
    private IWyImpSqeService gwjImpSqeService;

    @Resource
    protected SqlSessionFactory sqlSessionFactory;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeHouseOwnerByHouseCodes(String[] houseCodes) {
        ownerService.remove(QueryWrapper.create().where(WY_BASE_OWNER.HOUSE_CODE.in(houseCodes)));
        houseService.remove(QueryWrapper.create().where(WY_BASE_HOUSE.HOUSE_CODE.in(houseCodes)));
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveHouseOwner(WyBaseHouse house, WyBaseOwner owner) {
        //获取房屋序列
        house.setHouseCode(WyCustomImportConstants.SELFBUILTHOUSES + gwjImpSqeService.reserveSqeByIndex(WyCustomImportConstants.selfImpHouseSeqName, 1L));
        house.setImportFlag(WyConstants.YES);
        owner.setHouseCode(house.getHouseCode());
        owner.setImportFlag(WyConstants.YES);
        houseService.save(house);
        ownerService.save(owner);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateHouseOwner(WyBaseHouse house, WyBaseOwner owner) {
        houseService.updateById(house);
        ownerService.updateById(owner);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean savBuild(WyBaseBuild build) {
        build.setBuildCode(WyCustomImportConstants.SELFBUILTHOUSES + gwjImpSqeService.reserveSqeByIndex(WyCustomImportConstants.selfImpBuildSeqName, 1L));
        return buildService.save(build);
    }

    @Override
    public String importBaseCommunity(MultipartFile file, boolean updateSupport, boolean outsideFlag) throws Exception {
        ExcelResult<WyBaseCommunityExcelImpVO> excelResult = ExcelUtil.importExcel(file.getInputStream(), WyBaseCommunityExcelImpVO.class, true);
        List<WyBaseCommunityExcelImpVO> baseCommunityList = excelResult.getList();
        // 操作员
        String operName;
        if (outsideFlag) {
            operName = SecurityUtils.getLoginUser().getSysOutsideUser().getRealName();
        } else {
            operName = SecurityUtils.getLoginUser().getSysUser().getRealName();
        }
        return importBaseCommunity(BeanUtil.copyToList(baseCommunityList, WyBaseCommunity.class), updateSupport, operName);
    }

    /**
     * 导入基础项目信息
     *
     * @param baseCommunityList 基础项目列表
     * @param updateSupport     是否覆盖
     * @return
     */
    private String importBaseCommunity(List<WyBaseCommunity> baseCommunityList, boolean updateSupport, String operName) {
        if (StringUtils.isNull(baseCommunityList) || baseCommunityList.size() == 0) {
            throw new ServiceException("导入项目数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        String areaId = SecurityUtils.getAreaId();
        Long userId = SecurityUtils.getUserId();
        for (WyBaseCommunity bc : baseCommunityList) {
            if (StringUtils.isEmpty(bc.getCommunityName()) || StringUtils.isNull(bc.getAreaId()) || StringUtils.isNull(bc.getStreetId())) {
                failureMsg.append("导入失败！数据不能存在项目名、地区名、街道名称为空的数据！");
                throw new ServiceException(failureMsg.toString());
            }
            if (userId != 1) {
                if (!areaId.contains(Long.toString(bc.getAreaId()))) {
                    throw new ServiceException("导入失败！当前导入区域不在用户操作范围内！");
                }
            }
            try {
                WyBaseCommunity checkBc = communityService.getOne(QueryWrapper.create().where(WY_BASE_COMMUNITY.COMMUNITY_NAME.eq(bc.getCommunityName())));
                if (StringUtils.isNull(checkBc)) {
                    BeanValidators.validateWithException(validator, bc);
                    bc.setImportFlag(WyConstants.YES);
                    bc.setCreateBy(operName);
                    communityService.save(bc);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、小区名称： " + bc.getCommunityName() + " 导入新增成功");
                } else if (updateSupport) {
                    // 存在进行更新
                    bc.setCommunityId(checkBc.getCommunityId());
                    bc.setUpdateBy(operName);
                    communityService.updateById(bc);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、小区名称： " + bc.getCommunityName() + " 导入更新成功");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、小区名称： " + bc.getCommunityName() + " 导入失败：";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }

        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        return successMsg.toString();
    }

    @Override
    public String importDataBuild(MultipartFile file, boolean updateSupport, Long communityId, boolean outsideFlag) throws Exception {
        ExcelResult<WyBaseBuildExcelImpVO> excelResult = ExcelUtil.importExcel(file.getInputStream(), WyBaseBuildExcelImpVO.class, true);
        List<WyBaseBuildExcelImpVO> baseCommunityList = excelResult.getList();
        // 操作员
        String operName;
        if (outsideFlag) {
            operName = SecurityUtils.getLoginUser().getSysOutsideUser().getRealName();
        } else {
            operName = SecurityUtils.getLoginUser().getSysUser().getRealName();
        }
        return importBaseBuild(BeanUtil.copyToList(baseCommunityList, WyBaseBuild.class), updateSupport, communityId, operName);
    }

    /**
     * 导入楼栋信息
     *
     * @param BaseBuildList 楼栋项目列表
     * @param updateSupport 是否覆盖
     * @param communityId   项目ID
     * @return
     */
    private String importBaseBuild(List<WyBaseBuild> BaseBuildList, boolean updateSupport, Long communityId, String operName) {
        if (StringUtils.isNull(communityId)) {
            throw new ServiceException("项目编号不能为空");
        }
        // 查询项目信息
        WyBaseCommunity baseCommunity = communityService.getById(communityId);
        if (StringUtils.isNull(baseCommunity)) {
            throw new ServiceException("未获取到对应项目信息");
        }
        if (StringUtils.isNull(BaseBuildList) || BaseBuildList.size() == 0) {
            throw new ServiceException("导入楼栋数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        // 地区ID
        Long areaId = baseCommunity.getAreaId();
        // 街道ID
        Long streetId = baseCommunity.getStreetId();
        List<WyBaseBuild> inBuildData = new ArrayList<WyBaseBuild>();
        for (WyBaseBuild bb : BaseBuildList) {
            if (StringUtils.isEmpty(bb.getBuildName()) || StringUtils.isNull(bb.getTotalOwner())) {
                failureMsg.append("导入失败！不能存在楼栋名称、业主总户数为空的数据！");
                throw new ServiceException(failureMsg.toString());
            }
            try {
                WyBaseBuild checkBb = buildService.getOne(QueryWrapper.create().where(WY_BASE_BUILD.BUILD_NAME.eq(bb.getBuildName())));
                if (StringUtils.isNull(checkBb)) {
                    BeanValidators.validateWithException(validator, bb);
                    // 设置项目ID
                    bb.setCommunityId(communityId);
                    // 进行插入转换(imp+id)
                    bb.setProjectCode(WyCustomImportConstants.SELFBUILTHOUSES + communityId);
                    bb.setAreaId(areaId);
                    bb.setStreetId(streetId);
                    // 20230223 是否自建
                    bb.setImportFlag(WyConstants.YES);
                    bb.setCreateBy(operName);
                    inBuildData.add(bb);
                } else if (updateSupport) {
                    bb.setBuildId(checkBb.getBuildId());
                    bb.setUpdateBy(operName);
                    bb.setUpdateTime(DateUtils.getNowDate());
                    buildService.updateById(bb);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、楼栋名称： " + bb.getBuildName() + " 导入更新成功");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、楼栋名称： " + bb.getBuildName() + " 导入失败：";
                failureMsg.append(msg + e.getMessage());
                log.error(msg, e);
            }
        }
        // 操作导入楼栋
        importBuildList(inBuildData, successNum, failureNum, successMsg, failureMsg);
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        return successMsg.toString();
    }

    /**
     * 新增导入楼栋数据
     *
     * @param inBuildData 楼栋数据
     * @param successNum  成功数
     * @param failureNum  失败数
     * @param successMsg  成功信息
     * @param failureMsg  失败信息
     */
    private void importBuildList(List<WyBaseBuild> inBuildData, int successNum, int failureNum, StringBuilder successMsg, StringBuilder failureMsg) {
        if (inBuildData != null && inBuildData.size() > 0) {
            // 按导入数据量预约序列长度
            Integer size = inBuildData.size();
            // 预定返回后的第一个序列
            Long startSqe = gwjImpSqeService.reserveSqeByIndex(WyCustomImportConstants.selfImpBuildSeqName, size.longValue());
            if (startSqe == -1) {
                throw new ServiceException("未获取到操作序列权限！请稍后重试！");
            }
            for (WyBaseBuild bb : inBuildData) {
                try {
                    // 进行插入转换
                    bb.setBuildCode(WyCustomImportConstants.SELFBUILTHOUSES + startSqe);
                    buildService.save(bb);
                    startSqe++;
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、楼栋名称： " + bb.getBuildName() + " 导入新增成功");
                } catch (Exception e) {
                    failureNum++;
                    String msg = "<br/>" + failureNum + "、楼栋名称： " + bb.getBuildName() + " 导入失败：";
                    failureMsg.append(msg + e.getMessage());
                    log.error(msg, e);
                }
            }
        }
    }

    /**
     * 保存导入的房屋和业主
     *
     * @param inHouseData
     * @param inOwnerData
     */
    private void saveImportHouseAndOwner(List<WyBaseHouse> inHouseData, List<WyBaseOwner> inOwnerData) {
        if ((StringUtils.isNull(inHouseData) && inHouseData.size() == 0) || (StringUtils.isNull(inOwnerData) && inOwnerData.size() == 0)) {
            return;
        }
        // 按导入数据量预约序列长度
        Integer size = inHouseData.size();
        // 预定返回后的第一个序列
        Long startSqe = gwjImpSqeService.reserveSqeByIndex(WyCustomImportConstants.selfImpHouseSeqName, size.longValue());
        if (startSqe == -1) {
            throw new ServiceException("未获取到操作序列权限！请稍后重试！");
        }
        // 设置ExecutorType.BATCH原理：把SQL语句发个数据库，数据库预编译好，数据库等待需要运行的参数，接收到参数后一次运行，ExecutorType.BATCH只打印一次SQL语句，多次设置参数步骤
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            for (int i = 0; i < inHouseData.size(); i++) {
                String houseCode = WyCustomImportConstants.SELFBUILTHOUSES + startSqe;
                inHouseData.get(i).setHouseCode(houseCode);
                inOwnerData.get(i).setHouseCode(houseCode);
                startSqe++;
            }
            houseService.saveBatch(inHouseData);
            ownerService.saveBatch(inOwnerData);
            session.commit();
        } catch (Exception e) {
            session.rollback();
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /**
     * 修改导入的房屋和业主
     *
     * @param inHouseData
     * @param inOwnerData
     */
    private void upImportHouseAndOwner(List<WyBaseHouse> inHouseData, List<WyBaseOwner> inOwnerData) {
        if ((StringUtils.isNull(inHouseData) && inHouseData.size() == 0) || (StringUtils.isNull(inOwnerData) && inOwnerData.size() == 0)) {
            return;
        }
        // 设置ExecutorType.BATCH原理：把SQL语句发个数据库，数据库预编译好，数据库等待需要运行的参数，接收到参数后一次运行，ExecutorType.BATCH只打印一次SQL语句，多次设置参数步骤
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            houseService.updateBatch(inHouseData);
            ownerService.updateBatch(inOwnerData);
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }


    /*
     * 20230802
     */
    @Override
    public String importDataHouseAndOwner(MultipartFile file, boolean updateSupport, Long areaId, String buildCode, boolean outsideFlag) throws Exception {
        // TODO Auto-generated method stub
        // 获取对应楼栋信息
        WyBaseBuild gbb = buildService.getOne(QueryWrapper.create().where(WY_BASE_BUILD.BUILD_CODE.eq(buildCode)).and(WY_BASE_BUILD.AREA_ID.eq(areaId)));
        if (StringUtils.isNull(gbb)) {
            throw new ServiceException("未获取到对应地区下楼栋标识码为[" + buildCode + "]楼栋");
        }
        // 操作员
        String operName;
        if (outsideFlag) {
            operName = SecurityUtils.getLoginUser().getSysOutsideUser().getRealName();
        } else {
            operName = SecurityUtils.getLoginUser().getSysUser().getRealName();
        }
        return processImportDataHouseAndOwner(file, updateSupport, gbb, operName);
    }

    /**
     * 操作导入房屋和业主数据
     *
     * @param file          导入文件
     * @param updateSupport 是否覆盖
     * @return
     * @throws Exception
     */
    private String processImportDataHouseAndOwner(MultipartFile file, boolean updateSupport, WyBaseBuild gbb, String operName) throws Exception {
        ExcelResult<WyBaseHouseOwnerExcelImpVO> excelResult = ExcelUtil.importExcel(file.getInputStream(), WyBaseHouseOwnerExcelImpVO.class, true);
        List<WyBaseHouseOwnerExcelImpVO> gwjBaseHouseAndGwjBaseOwnerList = excelResult.getList();
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        // 缓存新增组
        List<WyBaseHouse> inHouseData = new ArrayList<WyBaseHouse>();
        List<WyBaseOwner> inOwnerData = new ArrayList<WyBaseOwner>();
        // 缓存更新组
        List<WyBaseHouse> upHouseData = new ArrayList<WyBaseHouse>();
        List<WyBaseOwner> upOwnerData = new ArrayList<WyBaseOwner>();
        for (WyBaseHouseOwnerExcelImpVO hao : gwjBaseHouseAndGwjBaseOwnerList) {
            if (StringUtils.isEmpty(hao.getHouseLocation()) || StringUtils.isEmpty(hao.getRoomNum()) || StringUtils.isNull(hao.getActualSize()) || StringUtils.isEmpty(hao.getOwnerName()) || StringUtils.isEmpty(hao.getOwnerPhone())) {
                failureMsg.append("[" + hao.getRoomNum() + "]" + "导入失败！必填项数据不能为空！");
                throw new ServiceException(failureMsg.toString());
            }
            try {
                // 校验导入参数
                BeanValidators.validateWithException(validator, hao);
                // 1新建房屋
                WyBaseHouse bh = new WyBaseHouse();
                // 设置楼栋标识码
                bh.setBuildCode(gbb.getBuildCode());
                // 设置地区
                bh.setAreaId(gbb.getAreaId());
                // 设置街道
                bh.setStreetId(gbb.getStreetId());
                // 坐落
                bh.setHouseLocation(hao.getHouseLocation());
                // 房屋号
                bh.setRoomNum(hao.getRoomNum());
                // 实测面积
                bh.setActualSize(hao.getActualSize());
                // 套内面积
                bh.setInsideSize(hao.getInsideSize());
                // 20230223 是否自建导入
                bh.setImportFlag(WyConstants.YES);
                // 2新建业主
                WyBaseOwner bo = new WyBaseOwner();
                // 设置地区
                bo.setAreaId(gbb.getAreaId());
                // 设置街道
                bo.setStreetId(gbb.getStreetId());
                // 名称
                bo.setOwnerName(hao.getOwnerName());
                // 身份证号
                bo.setCardNum(hao.getCardNum());
                // 手机号
                bo.setOwnerPhone(hao.getOwnerPhone());
                // 20230223 是否自建导入
                bo.setImportFlag(WyConstants.YES);
                // 查看房牌号是否存在[增加楼栋编号进行定位]
                WyBaseHouse checkBh = houseService.getOne(QueryWrapper.create().where(WY_BASE_HOUSE.ROOM_NUM.eq(hao.getRoomNum()).and(WY_BASE_HOUSE.BUILD_CODE.eq(gbb.getBuildCode()))));
                if (StringUtils.isNull(checkBh)) {
                    // 设置新增人员
                    bh.setCreateBy(operName);
                    bo.setCreateBy(operName);
                    inHouseData.add(bh);
                    inOwnerData.add(bo);
                    // 新增
                    if (inHouseData.size() == 100 && inOwnerData.size() == 100) {
                        saveImportHouseAndOwner(inHouseData, inOwnerData);
                        inHouseData.clear();
                        inOwnerData.clear();
                    }
                } else if (updateSupport) {
                    // 房屋更新则进行值替换
                    bh.setHouseId(checkBh.getHouseId());
                    bh.setHouseCode(checkBh.getHouseCode());
                    bh.setUpdateBy(operName);
                    // 业主更新则进行值替换
                    bo.setHouseCode(checkBh.getHouseCode());
                    bo.setUpdateBy(operName);
                    upHouseData.add(bh);
                    upOwnerData.add(bo);
                    // 修改
                    if (upHouseData.size() == 100 && upOwnerData.size() == 100) {
                        upImportHouseAndOwner(upHouseData, upOwnerData);
                        upHouseData.clear();
                        upOwnerData.clear();
                    }
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、楼栋： " + gbb.getBuildName() + ",房屋和业主数据导入失败：";
                log.error(msg, e);
            }
        }
        // 对余留数据进行新增
        if (inHouseData.size() > 0 && inOwnerData.size() > 0) {
            saveImportHouseAndOwner(inHouseData, inOwnerData);
        }
        // 对余留数据进行修改
        if (upHouseData.size() > 0 && upOwnerData.size() > 0) {
            upImportHouseAndOwner(upHouseData, upOwnerData);
        }
        if (failureNum > 0) {
            throw new ServiceException("很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确");
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！");
        }
        return successMsg.toString();
    }

}
